package com.ccf.business.etl.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.csv.CsvData;
import cn.hutool.core.text.csv.CsvReader;
import cn.hutool.core.text.csv.CsvRow;
import cn.hutool.core.text.csv.CsvUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ccf.business.etl.flow.ParamManager;
import com.ccf.business.etl.mapper.NodeMapper;
import com.ccf.business.etl.model.Node;
import com.ccf.business.etl.model.dto.NodeDTO;
import com.ccf.business.etl.service.INodeService;
import com.common.model.exception.ServiceException;
import com.common.model.pojo.Query;
import com.common.model.pojo.ResultVO;
import com.common.model.result.CodeEnum;
import com.common.model.result.PageResult;
import com.common.utils.constant.Constants;
import com.common.utils.constant.FileType;
import com.component.api.constants.ComponentConstants;
import com.component.api.constants.FlowConstants;
import com.component.api.model.dto.FileCenterConfigDTO;
import com.component.api.model.flow.NodeInfo;
import com.framework.minio.service.MinioService;
import com.framework.redis.service.RedisService;
import com.google.common.collect.Maps;
import com.service.database.model.DataColumn;
import com.service.database.model.DataModel;
import com.service.mybatis.service.impl.SuperServiceImpl;
import com.service.mybatis.util.Condition;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 节点表
 *
 * @author ccf
 * @date 2021-11-29 18:02:10
 */
@Slf4j
@Service
public class NodeServiceImpl extends SuperServiceImpl<NodeMapper, Node> implements INodeService {

    @Autowired
    private MinioService minioService;

    @Autowired
    private ParamManager paramManager;

    @Autowired
    private RedisService redisService;

    /**
    * 列表
    * @param query
    * @return
    */
    @Override
    public PageResult<Node> findList(Query query){
        Page<Node> page = page(Condition.getPage(query), Condition.getQueryWrapper(query));
        return PageResult.<Node>builder().datas(page.getRecords()).code(CodeEnum.SUCCESS.getCode()).count(page.getTotal()).build();
    }

    @Override
    public Map<String, String> copy(String flowId, String jobId) {
        Map<String, String> idMap = new HashMap<>();
        Map<String, Object> paramMap = Maps.newHashMap();
        paramMap.put("flow_id", flowId);
        List<Node> newNodes = new ArrayList<>();
        List<Node> nodes = listByMap(paramMap);
        for (Node node : nodes) {
            //任务执行不再解析节点
            try {
                NodeInfo nodeInfo = paramManager.getHandler(new NodeDTO(node)).get();
                String newId = IdUtil.fastSimpleUUID();
                Node newNode = Node.builder().flowId(jobId)
                        .rule(nodeInfo.getRule())
                        .name(nodeInfo.getName())
                        .params(node.getParams())
                        .runParams(nodeInfo.getParam())
                        .build();
                newNode.setId(newId);
                newNodes.add(newNode);
                idMap.put(node.getId(), newId);
            } catch (Exception e) {
                e.printStackTrace();
                throw new ServiceException(node.getName() + "参数错误:"+e.getMessage());
            }
        }
        if (newNodes.size() > 0){
            saveBatch(newNodes);
        } else {
            throw new ServiceException("流程内容为空，不能执行");
        }
        return idMap;
    }

    @Override
    public DataModel viewTable(String file, String header, String encoding) {
        List<DataColumn> columns = null;
        List<Map<String, Object>> datas = new ArrayList<>();
        InputStream inputStream = null;
        FileType fileType = FileType.enumOf(file);
        try {
            inputStream = minioService.getFileStream(file);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("文件读取失败" );
        }
        Boolean isHeader = isHeader(header);
        int b = isHeader ? 1 : 0;
        if(FileType.CSV.equals(fileType)){

            CsvReader csvReader = CsvUtil.getReader();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName(encoding));
            CsvData read = csvReader.read(inputStreamReader);
            List<CsvRow> rows = read.getRows();
            if (rows.size() == 0){
                throw new RuntimeException("文件内容为空" );
            }
            CsvRow csvRow1 = rows.get(0);
            List<String> headers = getHeader(csvRow1, isHeader);
            columns = getTableColumns(headers);
            for (int i = b; i < rows.size(); i++) {
                CsvRow csvRow = rows.get(i);
                List<String> rawList = csvRow.getRawList();
                if (rawList.size() != headers.size()){
                    continue;
                }
                Map<String, Object> data = new HashMap<>();
                for (int j = 0; j < rawList.size(); j++) {
                    data.put(headers.get(j), rawList.get(j));
                }
                datas.add(data);
            }
        }else if(FileType.XLS.equals(fileType) || FileType.XLSX.equals(fileType)){
            ExcelReader reader = ExcelUtil.getReader(inputStream);
            List<String> headers = getHeader(reader, isHeader);
            columns = getTableColumns(headers);
            List<List<Object>> read = reader.read(b);
            if (read.size() == 0){
                throw new RuntimeException("文件内容为空" );
            }
            for (int i = 0; i < read.size(); i++) {
                List<Object> objects = read.get(i);
                Map<String, Object> data = new HashMap<>();
                if (objects.size() != headers.size()){
                    continue;
                }
                for (int j = 0; j < objects.size(); j++) {
                    data.put(headers.get(j), objects.get(j));
                }
                datas.add(data);
            }
        } else {
            throw new RuntimeException("不支持预览格式" + fileType.getValue());
        }
       return DataModel.builder().columns(columns).datas(datas).build();
    }

    /**
     * 转换列
     * @param headers
     * @return
     */
    private List<DataColumn> getTableColumns(List<String> headers) {
        List<DataColumn> columns = new ArrayList<>();
        for (int i = 0; i < headers.size(); i++) {
            DataColumn map = new DataColumn();
            String header = headers.get(i);
            map.setColumnName(header);
            map.setDataType(ComponentConstants.VARCHAR);
            map.setLength(ComponentConstants.DEFAULT_VARCHAR_LENGTH);
            columns.add(map);
        }
        return columns;
    }

    @Override
    public ResultVO viewResult(String nodeId) {
        ResultVO resultVO = new ResultVO();
        List<String> columns = new ArrayList<>();
        List<Map<String, Object>> datas = new ArrayList<>();
        List<Object> objects = redisService.lGet(FlowConstants.KEY_ + nodeId, 0, -1);
        for (int i = 0; i < objects.size() ; i++) {
            Object o = objects.get(i);
            if (o == null){
                continue;
            }
            String str = o.toString();
            if (i == 0 ){
                String[] split = StrUtil.split(str, Constants.COMMA);
                CollUtil.addAll(columns, split);
            } else {
                str =  StrUtil.sub(str, 1, str.length() - 1 );
                String[] split = StrUtil.split(str, Constants.DUBLE_COMMA);
                Map<String, Object> map = new HashMap<>();
                for (int j = 0; j <split.length ; j++) {
                    String val = split[j];
                    if ("null".equalsIgnoreCase(val)){
                        val = null;
                    }
                    map.put(columns.get(j), val);
                }
                datas.add(map);
            }
        }
        resultVO.setColumns(columns);
        resultVO.setDatas(datas);
        return resultVO;
    }

    /**
     * 是否为表头
     * @param header
     * @return
     */
    public  Boolean isHeader(String header) {
        return Boolean.valueOf(FileCenterConfigDTO.HEADER.equalsIgnoreCase(header));
    }

    /**
     * excle查询表头
     * @param reader
     * @param isHeader
     * @return
     */
    public  List<String> getHeader(ExcelReader reader, Boolean isHeader) {
        List<String> headers = new ArrayList<>();
        List<Object> objects = reader.readRow(0);
        if (objects.size() == 0){
            throw new RuntimeException("文件内容为空" );
        }
        if (isHeader) {
            headers = objects.stream().map(e->String.valueOf(e)).collect(Collectors.toList());
        } else {
            for (int i = 0; i < objects.size(); i++) {
                headers.add(ComponentConstants.COLUMN_PREFIX + (i+1));
            }
        }
        return headers;
    }

    /**
     * csv获取文件头
     */
    public List<String> getHeader(CsvRow csvRow1, boolean isHeader) {
        List<String> headers = new ArrayList<>();
        List<String> rawList = csvRow1.getRawList();
        if (rawList.size() == 0){
            throw new RuntimeException("文件内容为空" );
        }
        if (isHeader) {
            headers = rawList;
        } else {
            for (int i = 0; i < rawList.size(); i++) {
                headers.add(ComponentConstants.COLUMN_PREFIX + (i+1));
            }
        }
        return headers;
    }
}
